home *** CD-ROM | disk | FTP | other *** search
/ Reverse Code Engineering RCE CD +sandman 2000 / ReverseCodeEngineeringRceCdsandman2000.iso / RCE / Library / Manuels & Misc / Assembly / AOA.ZIP / CH08 / EX8.ASM < prev    next >
Encoding:
Assembly Source File  |  1996-02-06  |  17.8 KB  |  599 lines

  1. ; Sample program for Chapter Eight
  2. ; Demonstrates the use of many MASM features discussed in Chapter Six
  3. ; including label types, constants, segment ordering, procedures, equates,
  4. ; address expressions, coercion and type operators, segment prefixes,
  5. ; the assume directive, conditional assembly, macros, listing directives,
  6. ; separate assembly, and using the UCR Standard Library.
  7. ;
  8. ; Include the header files for the UCR Standard Library.  Note that the
  9. ; "stdlib.a" file defines two segments; MASM will load these segments into
  10. ; memory before "dseg" in this program.
  11. ;
  12. ; The ".nolist" directive tells MASM not to list out all the macros for
  13. ; the standard library when producing an assembly listing.  Doing so would
  14. ; increase the size of the listing by many tens of pages and would tend to
  15. ; obscure the real code in this program.
  16. ;
  17. ; The ".list" directive turns the listing back on after MASM gets past the
  18. ; standard library files.  Note that these two directives (".nolist" and
  19. ; ".list") are only active if you produce an assembly listing using MASM's
  20. ; "/Fl" command line parameter.
  21.  
  22.  
  23.         .nolist
  24.         include     stdlib.a
  25.         includelib    stdlib.lib
  26.         .list
  27.  
  28.  
  29.  
  30. ; The following statement includes the special header file for this
  31. ; particular program.  The header file contains external definitions
  32. ; and various data type definitions.
  33.  
  34.         include        matrix.a
  35.  
  36.  
  37. ; The following two statements allow us to use 80386 instructions
  38. ; in the program.  The ".386" directive turns on the 80386 instruction
  39. ; set, the "option" directive tells MASM to use 16-bit segments by
  40. ; default (when using 80386 instructions, 32-bit segments are the default).
  41. ; DOS real mode programs must be written using 16-bit segments.
  42.  
  43.         .386
  44.         option    segment:use16
  45.  
  46.  
  47.  
  48. dseg        segment    para public 'data'
  49.  
  50. Rows        integer    ?        ;Number of rows in matrices
  51. Columns        integer    ?        ;Number of columns in matrices
  52.  
  53.  
  54. ; Input line is an input buffer this code uses to read a string of text
  55. ; from the user.  In particular, the GetWholeNumber procedure passes the
  56. ; address of InputLine to the GETS routine that reads a line of text
  57. ; from the user and places each character into this array.  GETS reads
  58. ; a maximum of 127 characters plus the enter key from the user.  It zero
  59. ; terminates that string (replacing the ASCII code for the ENTER key with
  60. ; a zero).  Therefore, this array needs to be at least 128 bytes long to
  61. ; prevent the possibility of buffer overflow.
  62. ;
  63. ; Note that the GetArray module also uses this array.
  64.  
  65. InputLine    char    128 dup (0)
  66.  
  67.  
  68. ; The following two pointers point at arrays of integers.
  69. ; This program dynamically allocates storage for the actual array data
  70. ; once the user tells the program how big the arrays should be.  The
  71. ; Rows and Columns variables above determine the respective sizes of
  72. ; these arrays.  After allocating the storage with a call to MALLOC,
  73. ; this program stores the pointers to these arrays into the following
  74. ; two pointer variables.
  75.  
  76. RowArray    dword    ?        ;Pointer to Row values
  77. ColArray    dword    ?        ;Pointer to column values.
  78.  
  79.  
  80.  
  81. ; ResultArrays is an array of dope vectors(*) to hold the results
  82. ; from the matrix operations:
  83. ;
  84. ; [0]- addition table
  85. ; [1]- subtraction table
  86. ; [2]- multiplication table
  87. ; [3]- division table
  88. ;
  89. ; [4]- modulo (remainder) table -- if the symbol "DoMOD" is defined.
  90. ;
  91. ; The equate that follows the ResultArrays declaration computes the number
  92. ; of elements in the array.  "$" is the offset into dseg immediately after
  93. ; the last byte of ResultArrays.  Subtracting this value from ResultArrays
  94. ; computes the number of bytes in ResultArrays.  Dividing this by the size
  95. ; of a single dope vector produces the number of elements in the array.
  96. ; This is an excellent example of how you can use address expressions in
  97. ; an assembly language program.
  98. ;
  99. ; The IFDEF DoMOD code demonstrates how easy it is to extend this matrix.
  100. ; Defining the symbol "DoMOD" adds another entry to this array.  The
  101. ; rest of the program adjusts for this new entry automatically.
  102. ;
  103. ; You can easily add new items to this array of dope vectors.  You will
  104. ; need to supply a title and a function to compute the matrice's entries.
  105. ; Other than that, however, this program automatically adjusts to any new
  106. ; entries you add to the dope vector array.
  107. ;
  108. ; (*) A "Dope Vector" is a data structure that describes a dynamically
  109. ; allocated array.  A typical dope vector contains the maximum value for
  110. ; each dimension, a pointer to the array data in memory, and some other
  111. ; possible information.  This program also stores a pointer to an array
  112. ; title and a pointer to an arithmetic function in the dope vector.
  113.  
  114.  
  115. ResultArrays    DopeVec    {AddTbl,Addition}, {SubTbl,Subtraction}
  116.         DopeVec    {MulTbl,Multiplication}, {DivTbl,Division}
  117.  
  118.         ifdef    DoMOD
  119.         DopeVec    {ModTbl,Modulo}
  120.         endif
  121.  
  122. ; Add any new functions of your own at this point, before the following equate:
  123.  
  124.  
  125. RASize        =    ($-ResultArrays) / (sizeof DopeVec)
  126.  
  127.  
  128. ; Titles for each of the four (five) matrices.
  129.  
  130. AddTbl        char    "Addition Table",0
  131. SubTbl      char    "Subtraction Table",0
  132. MulTbl        char    "Multiplication Table",0
  133. DivTbl        char    "Division Table",0
  134.  
  135.         ifdef    DoMOD
  136. ModTbl        char    "Modulo (Remainder) Table",0
  137.         endif
  138.  
  139. ; This would be a good place to put a title for any new array you create.
  140.  
  141. dseg        ends
  142.  
  143.  
  144.  
  145.  
  146. ; Putting PrintMat inside its own segment demonstrates that you can have
  147. ; multiple code segments within a program.  There is no reason we couldn't
  148. ; have put "PrintMat" in CSEG other than to demonstrate a far call to a
  149. ; different segment.
  150.  
  151. PrintSeg    segment    para public 'PrintSeg'
  152.  
  153. ; PrintMat-    Prints a matrix for the cross product operation.
  154. ;
  155. ;        On Entry:
  156. ;
  157. ;            DS must point at DSEG.
  158. ;            DS:SI points at the entry in ResultArrays for the
  159. ;            array to print.
  160. ;
  161. ; The output takes the following form:
  162. ;
  163. ;    Matrix Title
  164. ;
  165. ;           <- column matrix values ->
  166. ;
  167. ;    ^      *------------------------*
  168. ;    |      |                        |
  169. ;    R      |                        |
  170. ;    o      | Cross Product Matrix   |
  171. ;       w      |       Values           |
  172. ;           |                        |
  173. ;       V      |                        |
  174. ;    a      |                        |
  175. ;    l      |                        |
  176. ;    u      |                        |
  177. ;    e      |                        |
  178. ;    s      |                        |
  179. ;    |      |                        |
  180. ;    v      *------------------------*
  181.  
  182.  
  183. PrintMat    proc    far
  184.         assume    ds:dseg
  185.  
  186.  
  187. ; Note the use of conditional assembly to insert extra debugging statements
  188. ; if a special symbol "debug" is defined during assembly.  If such a symbol
  189. ; is not defined during assembly, the assembler ignores the following
  190. ; statements:
  191.  
  192.         ifdef    debug
  193.         print
  194.         char    "In PrintMat",cr,lf,0
  195.         endif
  196.  
  197.  
  198. ; First, print the title of this table.  The TTL field in the dope vector
  199. ; contains a pointer to a zero terminated title string.  Load this pointer
  200. ; into es:di and call PUTS to print that string.
  201.  
  202.         putcr
  203.         les    di, [si].DopeVec.TTL
  204.         puts
  205.  
  206. ; Now print the column values.  Note the use of PUTISIZE so that each
  207. ; value takes exactly six print positions. The following loop repeats
  208. ; once for each element in the Column array (the number of elements in
  209. ; the column array is given by the Dim2 field in the dope vector).
  210.  
  211.         print                ;Skip spaces to move past the
  212.         char    cr,lf,lf,"       ",0    ; row values.
  213.  
  214.         mov    dx, [si].DopeVec.Dim2    ;# of times to repeat the loop.
  215.         les    di, ColArray        ;Base address of array.
  216. ColValLp:    mov    ax, es:[di]        ;Fetch current array element.
  217.         mov    cx, 6            ;Print the value using a
  218.         putisize            ; minimum of six positions.
  219.         add    di, 2            ;Move on to next array element.
  220.         dec    dx            ;Repeat this loop DIM2 times.
  221.         jne    ColValLp
  222.         putcr                ;End of column array output
  223.         putcr                ;Insert a blank line.
  224.  
  225. ; Now output each row of the matrix.  Note that we need to output the
  226. ; RowArray value before each row of the matrix.
  227. ;
  228. ; RowLp is the outer loop that repeats for each row.
  229.  
  230.         mov    Rows, 0            ;Repeat for 0..Dim1-1 rows.
  231. RowLp:        les    di, RowArray        ;Output the current RowArray
  232.         mov    bx, Rows        ; value on the left hand side
  233.         add    bx, bx            ; of the matrix.
  234.         mov    ax, es:[di][bx]        ;ES:DI is base, BX is index.
  235.         mov    cx, 5            ;Output using five positions.
  236.         putisize
  237.         print
  238.         char    ": ",0
  239.  
  240. ; ColLp is the inner loop that repeats for each item on each row.
  241.  
  242.         mov    Columns, 0        ;Repeat for 0..Dim2-1 columns.
  243. ColLp:        mov    bx, Rows        ;Compute index into the array
  244.         imul    bx, [si].DopeVec.Dim2    ; index := (Rows*Dim2 +
  245.         add    bx, Columns        ;        columns) * 2
  246.         add    bx, bx
  247.  
  248. ; Note that we only have a pointer to the base address of the array, so we
  249. ; have to fetch that pointer and index off it to access the desired array
  250. ; element.  This code loads the pointer to the base address of the array into
  251. ; the es:di register pair.
  252.  
  253.         les    di, [si].DopeVec.Data    ;Base address of array.
  254.         mov    ax, es:[di][bx]        ;Get array element
  255.  
  256. ; The functions that compute the values for the array store an 8000h into
  257. ; the array element if some sort of error occurs.  Of course, it is possible
  258. ; to produce 8000h as an actual result, but giving up a single value to
  259. ; trap errors is worthwhile.  The following code checks to see if an error
  260. ; occurred during the cross product.  If so, this code prints "  ****",
  261. ; otherwise, it prints the actual value.
  262.  
  263.         cmp    ax, 8000h        ;Check for error value
  264.         jne    GoodOutput
  265.         print
  266.         char    "  ****",0        ;Print this for errors.
  267.         jmp    DoNext
  268.  
  269. GoodOutput:    mov    cx, 6            ;Use six print positions.
  270.         putisize            ;Print a good value.
  271.  
  272. DoNext:        mov    ax, Columns        ;Move on to next array
  273.         inc    ax            ; element.
  274.         mov    Columns, ax
  275.         cmp    ax, [si].DopeVec.Dim2    ;See if we're done with
  276.         jb    ColLp            ; this column.
  277.  
  278.         putcr                ;End each column with CR/LF
  279.  
  280.         mov    ax, Rows        ;Move on to the next row.
  281.         inc    ax
  282.         mov    Rows, ax
  283.         cmp    ax, [si].DopeVec.Dim1    ;Have we finished all the
  284.         jb    RowLp            ; rows?  Repeat if not done.
  285.         ret
  286. PrintMat    endp
  287. PrintSeg    ends
  288.  
  289.  
  290.  
  291.  
  292.  
  293. cseg        segment    para public 'code'
  294.         assume    cs:cseg, ds:dseg
  295.  
  296.  
  297. ;GetWholeNum-    This routine reads a whole number (an integer greater than
  298. ;        zero) from the user.  If the user enters an illegal whole
  299. ;        number, this procedure makes the user re-enter the data.
  300.  
  301. GetWholeNum    proc    near
  302.         lesi    InputLine    ;Point es:di at InputLine array.
  303.         gets
  304.  
  305.         call    Geti        ;Get an integer from the line.
  306.         jc    BadInt        ;Carry set if error reading integer.
  307.         cmp    ax, 0        ;Must have at least one row or column!
  308.         jle    BadInt
  309.         ret
  310.  
  311. BadInt:        print
  312.         char    Bell
  313.         char    "Illegal integer value, please re-enter",cr,lf,0
  314.         jmp    GetWholeNum
  315. GetWholeNum    endp
  316.  
  317.  
  318.  
  319.  
  320. ; Various routines to call for the cross products we compute.
  321. ; On entry, AX contains the first operand, dx contains the second.
  322. ; These routines return their result in AX.
  323. ; They return AX=8000h if an error occurs.
  324. ;
  325. ; Note that the CrossProduct function calls these routines indirectly.
  326.  
  327. addition    proc    far
  328.         add    ax, dx
  329.         jno    AddDone        ;Check for signed arithmetic overflow.
  330.         mov    ax, 8000h    ;Return 8000h if overflow occurs.
  331. AddDone:    ret
  332. addition    endp
  333.  
  334.  
  335. subtraction    proc    far
  336.         sub    ax, dx
  337.         jno    SubDone
  338.         mov    ax, 8000h    ;Return 8000h if overflow occurs.
  339. SubDone:    ret
  340. subtraction    endp
  341.  
  342. multiplication    proc    far
  343.         imul    ax, dx
  344.         jno    MulDone
  345.         mov    ax, 8000h    ;Error if overflow occurs.
  346. MulDone:    ret
  347. multiplication    endp
  348.  
  349. division    proc    far
  350.         push    cx        ;Preserve registers we destory.
  351.  
  352.         mov    cx, dx
  353.         cwd
  354.         test    cx, cx        ;See if attempting division by zero.
  355.         je    BadDivide
  356.         idiv    cx
  357.  
  358.         mov    dx, cx        ;Restore the munged register.
  359.         pop    cx
  360.         ret
  361.  
  362. BadDivide:    mov    ax, 8000h
  363.         mov    dx, cx
  364.         pop    cx
  365.         ret
  366. division    endp
  367.  
  368.  
  369. ; The following function computes the remainder if the symbol "DoMOD"
  370. ; is defined somewhere prior to this point.
  371.  
  372.         ifdef    DoMOD
  373. modulo        proc    far
  374.         push    cx
  375.  
  376.         mov    cx, dx
  377.         cwd
  378.         test    cx, cx        ;See if attempting division by zero.
  379.         je    BadDivide
  380.         idiv    cx
  381.         mov    ax, dx        ;Need to put remainder in AX.
  382.         mov    dx, cx        ;Restore the munged registers.
  383.         pop    cx
  384.         ret
  385.  
  386. BadMod:        mov    ax, 8000h
  387.         mov    dx, cx
  388.         pop    cx
  389.         ret
  390. modulo        endp
  391.         endif
  392.  
  393.  
  394.  
  395. ; If you decide to extend the ResultArrays dope vector array, this is a good
  396. ; place to define the function for those new arrays.
  397.  
  398.  
  399.  
  400.  
  401.  
  402. ; The main program that reads the data from the user, calls the appropriate
  403. ; routines, and then prints the results.
  404.  
  405. Main        proc
  406.         mov    ax, dseg
  407.         mov    ds, ax
  408.         mov    es, ax
  409.         meminit
  410.  
  411.  
  412. ; Prompt the user to enter the number of rows and columns:
  413.  
  414. GetRows:    print
  415.         byte    "Enter the number of rows for the matrix:",0
  416.  
  417.         call    GetWholeNum
  418.         mov    Rows, ax
  419.  
  420. ; Okay, read each of the row values from the user:
  421.  
  422.         print
  423.         char    "Enter values for the row (vertical) array",cr,lf,0
  424.  
  425. ; Malloc allocates the number of bytes specified in the CX register.
  426. ; AX contains the number of array elements we want;  multiply this value
  427. ; by two since we want an array of words.  On return from malloc, es:di
  428. ; points at the array allocated on the "heap".  Save away this pointer in
  429. ; the "RowArray" variable.
  430. ;
  431. ; Note the use of the "wp" symbol. This is an equate to "word ptr" appearing
  432. ; in the "matrix.a" include file.  Also note the use of the address expression
  433. ; "RowArray+2" to access the segment portion of the double word pointer.
  434.  
  435.         mov    cx, ax
  436.         shl    cx, 1
  437.         malloc
  438.         mov    wp RowArray, di
  439.         mov    wp RowArray+2, es
  440.  
  441. ; Okay, call "GetArray" to read "ax" input values from the user.
  442. ; GetArray expects the number of values to read in AX and a pointer
  443. ; to the base address of the array in es:di.
  444.  
  445.         print
  446.         char    "Enter row data:",0
  447.  
  448.         mov    ax, Rows    ;# of values to read.
  449.         call    GetArray    ;ES:DI still points at array.
  450.  
  451.  
  452.  
  453. ; Okay, time to repeat this for the column (horizontal) array.
  454.  
  455. GetCols:    print
  456.         byte    "Enter the number of columns for the matrix:",0
  457.  
  458.         call    GetWholeNum    ;Get # of columns from the user.
  459.         mov    Columns, ax    ;Save away number of columns.
  460.  
  461.  
  462. ; Okay, read each of the column values from the user:
  463.  
  464.         print
  465.         char    "Enter values for the column (horz.) array",cr,lf,0
  466.  
  467. ; Malloc allocates the number of bytes specified in the CX register.
  468. ; AX contains the number of array elements we want;  multiply this value
  469. ; by two since we want an array of words.  On return from malloc, es:di
  470. ; points at the array allocated on the "heap".  Save away this pointer in
  471. ; the "RowArray" variable.
  472.  
  473.         mov    cx, ax            ;Convert # Columns to # bytes
  474.         shl    cx, 1            ; by multiply by two.
  475.         malloc                ;Get the memory.
  476.         mov    wp ColArray, di        ;Save pointer to the
  477.         mov    wp ColArray+2, es    ; columns vector (array).
  478.  
  479. ; Okay, call "GetArray" to read "ax" input values from the user.
  480. ; GetArray expects the number of values to read in AX and a pointer
  481. ; to the base address of the array in es:di.
  482.  
  483.         print
  484.         char    "Enter Column data:",0
  485.  
  486.         mov    ax, Columns        ;# of values to read.
  487.         call    GetArray        ;ES:DI points at column array.
  488.  
  489.  
  490. ; Okay, initialize the matrices that will hold the cross products.
  491. ; Generate RASize copies of the following code.
  492. ; The "repeat" macro repeats the statements between the "repeat" and the "endm"
  493. ; directives RASize times.  Note the use of the Item symbol to automatically
  494. ; generate different indexes for each repetition of the following code.
  495. ; The "Item = Item+1" statement ensures that Item will take on the values
  496. ; 0, 1, 2, ..., RASize on each repetition of this loop.
  497. ;
  498. ; Remember, the "repeat..endm" macro copies the statements multiple times
  499. ; within the source file, it does not execute a "repeat..until" loop at
  500. ; run time.  That is, the following macro is equivalent to making "RASize"
  501. ; copies of the code, substituting different values for Item for each
  502. ; copy.
  503. ;
  504. ; The nice thing about this code is that it automatically generates the
  505. ; proper amount of initialization code, regardless of the number of items
  506. ; placed in the ResultArrays array.
  507.  
  508.  
  509. Item        =    0
  510.  
  511.         repeat    RASize
  512.  
  513.         mov    cx, Columns        ;Compute the size, in bytes,
  514.         imul    cx, Rows        ; of the matrix and allocate
  515.         add    cx, cx            ; sufficient storage for the
  516.         malloc                ; array.
  517.  
  518.         mov    wp ResultArrays[Item * (sizeof DopeVec)].Data, di
  519.         mov    wp ResultArrays[Item * (sizeof DopeVec)].Data+2, es
  520.  
  521.         mov    ax, Rows
  522.         mov    ResultArrays[Item * (sizeof DopeVec)].Dim1, ax
  523.  
  524.         mov    ax, Columns
  525.         mov    ResultArrays[Item * (sizeof DopeVec)].Dim2, ax
  526.  
  527.         mov    ResultArrays[Item * (sizeof DopeVec)].ESize, 2
  528.  
  529. Item        =    Item+1
  530.         endm
  531.  
  532.  
  533.  
  534. ; Okay, we've got the input values from the user,
  535. ; now let's compute the addition, subtraction, multiplication,
  536. ; and division tables.  Once again, a macro reduces the amount of
  537. ; typing we need to do at this point as well as automatically handling
  538. ; however many items are present in the ResultArrays array.
  539.  
  540. element        =    0
  541.  
  542.         repeat    RASize
  543.         lfs    bp, RowArray        ;Pointer to row data.
  544.         lgs    bx, ColArray        ;Pointer to column data.
  545.  
  546.         lea    cx, ResultArrays[element * (sizeof DopeVec)]
  547.         call    CrossProduct
  548.  
  549. element        =    element+1
  550.         endm
  551.  
  552.  
  553. ; Okay, print the arrays down here.  Once again, note the use of the
  554. ; repeat..endm macro to save typing and automatically handle additions
  555. ; to the ResultArrays array.
  556.  
  557.  
  558. Item        =    0
  559.  
  560.         repeat    RASize
  561.         mov    si, offset ResultArrays[item * (sizeof DopeVec)]
  562.         call    PrintMat
  563. Item        =    Item+1
  564.         endm
  565.  
  566.  
  567. ; Technically, we don't have to free up the storage malloc'd for each
  568. ; of the arrays since the program is about to quit.  However, it's a
  569. ; good idea to get used to freeing up all your storage when you're done
  570. ; with it.  For example, were you to add code later at the end of this
  571. ; program, you would have that extra memory available to that new code.
  572.  
  573.         les    di, ColArray
  574.         free
  575.         les    di, RowArray
  576.         free
  577.  
  578. Item        =    0
  579.         repeat    RASize
  580.         les    di, ResultArrays[Item * (sizeof DopeVec)].Data
  581.         free
  582. Item        =    Item+1
  583.         endm
  584.  
  585.  
  586. Quit:        ExitPgm            ;DOS macro to quit program.
  587. Main        endp
  588.  
  589. cseg        ends
  590.  
  591. sseg        segment    para stack 'stack'
  592. stk        byte    1024 dup ("stack   ")
  593. sseg        ends
  594.  
  595. zzzzzzseg    segment    para public 'zzzzzz'
  596. LastBytes    byte    16 dup (?)
  597. zzzzzzseg    ends
  598.         end    Main
  599.